﻿using EntityMapperCreater.DataAccess;
using MySql.Data.MySqlClient;
using MySqlX.XDevAPI.Relational;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using static System.Net.WebRequestMethods;

namespace EntityMapperCreater
{
    public partial class MapperCreaterFrm : Form
    {
        private XmlDocument xmlDoc = new XmlDocument();

        public MapperCreaterFrm()
        {
            InitializeComponent();
            InitData();
        }

        private void MapperCreaterFrm_Load(object sender, EventArgs e)
        {
            this.tabControl1.SelectedTab = this.tabPage1;
        }

        private void chb_isnotify_CheckedChanged(object sender, EventArgs e)
        {
            wpfnotify = chb_isnotify.Checked ? 1 : 0;
        }

        private void btn_openfile_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                txtIP.Text = openFileDialog.FileName;
            }
        }

        /// <summary>
        /// 数据源
        /// </summary>
        private DataTable tableData = null;

        private void btn_refresh_Click(object sender, EventArgs e)
        {
            //初始化配置文件
            InitStaticData.sysConfig(this.cmb_db.Text);
            if (!InitStaticData.conn.checkDBconn())
            {
                MessageBox.Show("连接数据库失败，请检查配置参数!");
                return;
            }
            tableData = null;
            string sql = queryTableNamesql;
            IDbDataParameter[] sqlParameters;
            if (this.cmb_db.Text == "sqlite")
            {
                sql = sql.Replace("@tablename", tb_name.Text.Trim());
                sqlParameters = null;
            }
            else if (this.cmb_db.Text == "mysql")
            {
                sqlParameters = new MySqlParameter[]
                {
                    new MySqlParameter("@tablename", tb_name.Text.Trim())
                };
            }
            else
            {
                throw new Exception("请选择有效的数据库类型！");
            }
            tableData = InitStaticData.conn.Select(sql, sqlParameters);
            dg_table.DataSource = tableData;
        }

        private void dg_table_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            DataGridViewRow dr = dg_table.SelectedRows[0];
            this.tb_name.Text = dr.Cells[0].Value.ToString();
        }

        #region private Methods

        private void InitData()
        {
            xmlDoc.Load(InitStaticData.dbPath);
        }

        private void setXmlNodeInnerText(string nodeName, string value)
        {
            XmlNode node = xmlDoc.SelectSingleNode(nodeName);
            node.InnerText = value;
            xmlDoc.Save(InitStaticData.dbPath);
        }

        private string getProcExecSql(string procname)
        {
            //与type无关
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append($"call {procname} ");

            foreach (DataRow row in data.Rows)
            {
                stringBuilder.Append(row[0].ToString().Trim() + ",");
            }

            string sql = stringBuilder.ToString();

            return sql.Substring(0, sql.Length - 1);//去掉最后一个,
        }

        /// <summary>
        /// 根据表名生成mapper名
        /// </summary>
        /// <param name="tablename"></param>
        /// <returns></returns>
        private string getMapperNameFromTablename(string tablename)
        {
            if (string.IsNullOrWhiteSpace(tablename))
                return "";

            StringBuilder builder = new StringBuilder();

            var array = tablename.Split('_');
            foreach (var item in array)
            {
                builder.Append(item.Substring(0, 1).ToUpper() + item.Substring(1));
            }
            builder.Append("Mapper");

            return builder.ToString();
        }

        private int wpfnotify = 1;

        private bool getTableMapperFile(string tablename)
        {
            if (string.IsNullOrWhiteSpace(tablename))
                return false;

            var mapperName = getMapperNameFromTablename(tablename);

            string tab1 = "    ";
            string tab2 = "        ";
            string classType = cmb_classType.Text.Trim();

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("using System;\r\n");
            stringBuilder.Append($"using {this.tb_tmns.Text.Trim()};\r\n\r\n");
            stringBuilder.Append($"namespace {this.tb_mapperNs.Text.Trim()}\r\n");
            stringBuilder.Append("{\r\n");
            stringBuilder.Append(tab1 + "/// <summary>\r\n");
            stringBuilder.Append(tab1 + $"/// {tablename}表\r\n");
            stringBuilder.Append(tab1 + "/// <summary>\r\n");
            stringBuilder.Append(tab1 + $"{classType} class {mapperName} : TableModelMapper\r\n");
            stringBuilder.Append(tab1 + "{\r\n");

            #region 表字段
            stringBuilder.Append(tab2 + "#region 表字段\r\n");
            foreach (DataRow row in data.Rows)
            {
                string filed = row["name"].ToString().Trim();
                if (string.Equals("id", filed.ToLower())) continue;
                string type = row["type"].ToString().Trim().ToLower();
                string extra = "";
                if (type.Contains("char") || type.Contains("text"))
                {
                    type = "string";
                    extra = @" =""""";
                }
                else if (type.Contains("money") || type.Contains("numeric") || type.Contains("decimal"))
                {
                    type = "double";
                }
                else if (type.Contains("int"))
                {
                    type = "int";
                }
                else if (type.Contains("datetime"))
                {
                    type = "DateTime";
                }
                else
                {
                    //默认给string类型
                    //throw new Exception("未知的数据类型!");
                    type = "string";
                    extra = @" =""""";
                }
                stringBuilder.Append(tab2 + $"private {type} _{filed}{extra};\r\n");
            }
            stringBuilder.Append(tab2 + "#endregion\r\n\r\n");
            #endregion

            #region 属性
            foreach (DataRow row in data.Rows)
            {
                string filed = row["name"].ToString().Trim();
                if (string.Equals("id", filed.ToLower())) continue;
                //添加注释
                stringBuilder.Append(tab2 + "/// <summary>\r\n");
                stringBuilder.Append(tab2 + "/// \r\n");
                stringBuilder.Append(tab2 + "/// </summary>\r\n");

                string type = row["type"].ToString().Trim().ToLower();
                string valueDefault = $"_{filed}";
                if (type.Contains("char") || type.Contains("text"))
                {
                    type = "string";
                }
                else if (type.Contains("money") || type.Contains("numeric") || type.Contains("decimal"))
                {
                    type = "double";
                }
                else if (type.Contains("int"))
                {
                    type = "int";
                }
                else if (type.Contains("datetime"))
                {
                    type = "DateTime";
                    //DateTime类型set默认值
                    valueDefault = $@"(_{filed} == DateTime.MinValue) ? Convert.ToDateTime(""1900-01-01"") : _{filed}";
                }
                else
                {
                    //默认给string类型
                    //throw new Exception("未知的数据类型!");
                    type = "string";
                }
                string propertyName = String.Empty;
                //propertyName = filed.Substring(0, 1).ToUpper() + filed.Substring(1);
                var list = filed.Split('_').ToList();
                foreach (var item in list)
                {
                    propertyName += item.Substring(0, 1).ToUpper() + item.Substring(1);
                }
                //propertyName = propertyName.Substring(0, propertyName.Length - 1);
                if (wpfnotify == 1)
                {
                    stringBuilder.Append(tab2 + $"public {type} {propertyName}" +
                        " {" + $" get => {valueDefault}; set => Set(nameof({propertyName}), ref _{filed}, value); " + "}\r\n");
                }
                else
                {
                    stringBuilder.Append(tab2 + $"public {type} {propertyName}" +
                        " {" + $" get => {valueDefault}; set => _{filed} = value; " + "}\r\n");
                }
            }
            #endregion

            stringBuilder.Append(tab1 + "}\r\n");
            stringBuilder.Append("}");

            //保存到文件中
            WriteIntoFile($"{mapperName}.cs", stringBuilder.ToString());

            return true;
        }

        /// <summary>
        /// 数据库仓储
        /// </summary>
        /// <param name="tablename"></param>
        /// <returns></returns>
        private bool getTableMapperRespositoryFile(string tablename)
        {
            if (string.IsNullOrWhiteSpace(tablename))
                return false;

            var mapperName = getMapperNameFromTablename(tablename);
            var mapperRespositoryName = mapperName.Replace("Mapper", "Respository");

            string tab1 = "    ";
            string classType = cmb_classType.Text.Trim();

            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("using System;\r\n");
            stringBuilder.Append($"using {this.tb_mapperNs.Text.Trim()};\r\n\r\n");
            stringBuilder.Append("//<copyright project=\"You.project\" company=\"you\">\r\n// Copyright (c）YOU. All rights reserved.\r\n//</copyright>\r\n");
            stringBuilder.Append("//<author>you</author>\r\n");
            stringBuilder.Append($"//<date>{DateTime.Now.ToString("yyyy-MM-dd")}</date>\r\n");
            stringBuilder.Append("// 数据库仓储\r\n");
            stringBuilder.Append("//*********************************************************************************\r\n");
            stringBuilder.Append("//<remarks>note..</remarks>\r\n");
            stringBuilder.Append($"namespace {this.tb_RespositoryNs.Text.Trim()}\r\n");
            stringBuilder.Append("{\r\n");
            stringBuilder.Append(tab1 + "/// <summary>\r\n");
            stringBuilder.Append(tab1 + $"/// {tablename}表仓储\r\n");
            stringBuilder.Append(tab1 + "/// <summary>\r\n");
            stringBuilder.Append(tab1 + $"{classType} class {mapperRespositoryName} : BaseRespository<{mapperName}>");
            stringBuilder.Append(" { }\r\n");
            stringBuilder.Append("}");

            //保存到文件中
            WriteIntoFile($"{mapperRespositoryName}.cs", stringBuilder.ToString());

            return true;
        }

        private const string procedurePath = "ProcedureEntityMapper.xml";

        /// <summary>
        /// 创建mapper.xml
        /// </summary>
        /// <param name="tablename">mapper</param>
        /// <param name="dataList">内容</param>
        private void createTableMapperXmlFile(string tablename, List<MapperXmlEntity> dataList)
        {
            var mapperName = getMapperNameFromTablename(tablename);
            if (string.IsNullOrWhiteSpace(mapperName) || dataList == null || dataList.Count == 0)
                return;

            this.WriteToFileEnd(mapperName + ".xml", dataList);
        }

        #region 文件操作

        /// <summary>
        /// 检查是否存在文件夹
        /// </summary>
        /// <param name="mapperfile">文件</param>
        /// <returns></returns>
        private bool JudgeFileExist(string mapperfile)
        {
            bool flag = true;
            string path = Path.Combine(InitStaticData.mapperpath, mapperfile);
            if (!Directory.Exists(InitStaticData.mapperpath))
            {
                Directory.CreateDirectory(InitStaticData.mapperpath);
                flag = false;
            }
            if (!System.IO.File.Exists(path))
            {
                FileStream fs = System.IO.File.Create(path);
                fs.Close();
                flag = false;
            }

            return flag;
        }
        /// <summary>
        /// 覆盖写入文本文件
        /// </summary>
        /// <param name="mapperfile">文件名</param>
        /// <param name="context">内容</param>
        private void WriteIntoFile(string mapperfile, string context)
        {
            JudgeFileExist(mapperfile);
            string path = Path.Combine(InitStaticData.mapperpath, mapperfile);
            FileStream f = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
            StreamWriter sw = new StreamWriter(f);
            sw.WriteLine(context);
            sw.Flush();
            sw.Close();
            f.Close();
        }

        /// <summary>
        /// 写入文本文件
        /// 给xml文件使用
        /// </summary>
        /// <param name="mapperfile">文件名</param>
        /// <param name="context">内容</param>
        private void WriteToFileEnd(string mapperfile, List<MapperXmlEntity> dataList)
        {
            if (!JudgeFileExist(mapperfile))//原先xml文件不存在,则加入默认内容
            {
                StringBuilder stringBuilder = new StringBuilder();
                stringBuilder.Append(@"<?xml version=""1.0"" encoding=""UTF-8"" ?>" + "\r\n");
                stringBuilder.Append($@"<mapper namespace=""{this.tb_mapperNs.Text.Trim()}"">" + "\r\n");
                stringBuilder.Append("</mapper>");
                WriteIntoFile(mapperfile, stringBuilder.ToString());
            }
            try
            {
                string path = Path.Combine(InitStaticData.mapperpath, mapperfile);
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(path);
                var nodes = xmlDocument.SelectSingleNode("mapper");
                List<string> idList = new List<string>();
                if (nodes != null)
                {
                    foreach (var node in nodes)
                    {
                        if (!(node is XmlElement))
                            continue;
                        XmlElement ele = node as XmlElement;
                        idList.Add(ele.GetAttribute("id"));
                    }
                    //筛选出重复的id
                    dataList = dataList.Where(d => !idList.Contains(d.IdValue)).ToList();
                    foreach (var entity in dataList)
                    {
                        //创建节点
                        XmlElement xmlElement = xmlDocument.CreateElement(entity.NodeName);
                        xmlElement.SetAttribute("id", entity.IdValue);
                        xmlElement.SetAttribute("remark", entity.RemarkValue);
                        xmlElement.SetAttribute("log", entity.LogValue);
                        xmlElement.InnerText = entity.InnerText;
                        nodes.AppendChild(xmlElement);
                    }
                }
                xmlDocument.Save(path);
            }
            catch (Exception ex)
            {

            }
        }

        private List<MapperXmlEntity> GetMapperXmlEntities(string mapper)
        {
            if (string.IsNullOrWhiteSpace(mapper)) return null;
            //mapper = mapper.Substring(0, 1).ToUpper() + mapper.Substring(1);
            var list = mapper.Split('_').ToList();
            mapper = String.Empty;
            foreach (var item in list)
            {
                mapper += item.Substring(0, 1).ToUpper() + item.Substring(1);
            }
            List<MapperXmlEntity> mapperXmlEntities = new List<MapperXmlEntity>();
            switch (createType)
            {
                case CreateType.procedure:
                    string procedureContext = getProcExecSql(mapper);
                    mapperXmlEntities.Add(new MapperXmlEntity()
                    {
                        NodeName = "procedure",
                        IdValue = mapper,
                        RemarkValue = "mapper生成工具生成,业务sql以实际为准",
                        LogValue = "F",
                        InnerText = $"<![CDATA[{procedureContext}]]>",
                    });
                    break;
                case CreateType.table:
                default:
                    List<string> columns = new List<string>();
                    foreach (DataRow row in data.Rows)
                    {
                        columns.Add(row["name"].ToString().Trim());
                    }
                    string select = $"select {string.Join(",", columns)} from {tb_name.Text.Trim()}";
                    mapperXmlEntities.Add(new MapperXmlEntity()
                    {
                        NodeName = "select",
                        IdValue = $"GetList",
                        RemarkValue = "查找全部数据",
                        LogValue = "F",
                        InnerText = $"\r\n    {select}\r\n  ",
                        //InnerText = $"\r\n    {select}\r\n  ",
                    });
                    string selectById = $"select {string.Join(",", columns)} from {tb_name.Text.Trim()} where id=@id";
                    mapperXmlEntities.Add(new MapperXmlEntity()
                    {
                        NodeName = "select",
                        IdValue = $"SelectById",
                        RemarkValue = "通过Id查找数据",
                        LogValue = "F",
                        InnerText = $"\r\n    {selectById}\r\n  ",
                    });
                    string deleteById = $"delete from {tb_name.Text.Trim()} where id=@id";
                    mapperXmlEntities.Add(new MapperXmlEntity()
                    {
                        NodeName = "delete",
                        IdValue = $"DeleteById",
                        RemarkValue = "通过Id删除元素",
                        LogValue = "F",
                        InnerText = $"\r\n    {deleteById}\r\n  ",
                    });
                    string insert = $"insert into {this.tb_name.Text.Trim()} ({string.Join(",", columns.Where(c => !string.Equals("id", c.ToLower())))})"
                        + $"\r\n    values ({string.Join(",", columns.Where(c => !string.Equals("id", c.ToLower())).Select(s => $"@{s.Replace("_", "")}"))})";
                    mapperXmlEntities.Add(new MapperXmlEntity()
                    {
                        NodeName = "insert",
                        IdValue = $"Insert{mapper}",
                        RemarkValue = "mapper生成工具生成,业务sql以实际为准",
                        LogValue = "F",
                        InnerText = $"\r\n    {insert}\r\n  ",
                    });
                    string columnsString = string.Empty;
                    foreach (var column in columns)
                    {
                        if (string.Equals("id", column.ToLower())) continue;
                        columnsString += $"{column}=@{column.Replace("_","")},";
                    }
                    columnsString = columnsString.Substring(0, columnsString.Length - 1);
                    string update = $"update {tb_name.Text.Trim()} set {columnsString} where id=@id";//column1 = @value1,column2 = @value2
                    mapperXmlEntities.Add(new MapperXmlEntity()
                    {
                        NodeName = "update",
                        IdValue = $"Update{mapper}",
                        RemarkValue = "mapper生成工具生成,业务sql以实际为准",
                        LogValue = "F",
                        InnerText = $"\r\n    {update}\r\n  ",
                    });
                    break;
            }

            return mapperXmlEntities;
        }

        #endregion

        #endregion

        #region 第一步：配置

        private void tabPage1_Enter(object sender, EventArgs e)
        {
            try
            {
                if (cmb_db.Text == "sqlite")
                {
                    txtIP.Text = xmlDoc.SelectSingleNode("DBConfig/SQLITE/database").InnerText;
                    txtPwd.Text = xmlDoc.SelectSingleNode("DBConfig/SQLITE/password").InnerText;
                    txtName.Text = "";
                    txtName.Enabled = false;
                    txtUser.Text = "";
                    txtUser.Enabled = false;
                }
                else
                {
                    txtName.Enabled = true;
                    txtUser.Enabled = true;
                    txtIP.Text = xmlDoc.SelectSingleNode("DBConfig/MYSQL/IP").InnerText;
                    txtName.Text = xmlDoc.SelectSingleNode("DBConfig/MYSQL/database").InnerText;
                    txtUser.Text = xmlDoc.SelectSingleNode("DBConfig/MYSQL/userid").InnerText;
                    txtPwd.Text = xmlDoc.SelectSingleNode("DBConfig/MYSQL/PWD").InnerText;
                }

                tb_mapperpath.Text = InitStaticData.mapperpath;
            }
            catch (Exception ce)
            {
                MessageBox.Show("读取配置文件出错!" + ce.Message);
            }

        }

        private void btn1Next_Click(object sender, EventArgs e)
        {
            try
            {
                btn1Next.Enabled = false;
                if (cmb_db.Text == "sqlite")
                {
                    setXmlNodeInnerText("DBConfig/SQLITE/database", txtIP.Text.Trim());
                    setXmlNodeInnerText("DBConfig/SQLITE/password", txtPwd.Text.Trim());
                }
                else
                {
                    setXmlNodeInnerText("DBConfig/MYSQL/IP", txtIP.Text.Trim());
                    setXmlNodeInnerText("DBConfig/MYSQL/database", txtName.Text.Trim());
                    setXmlNodeInnerText("DBConfig/MYSQL/userid", txtUser.Text.Trim());
                    setXmlNodeInnerText("DBConfig/MYSQL/PWD", txtPwd.Text.Trim());
                }

                if (!string.IsNullOrWhiteSpace(tb_mapperpath.Text.Trim()))
                {
                    InitStaticData.mapperpath = tb_mapperpath.Text.Trim();
                }

                //初始化配置文件
                InitStaticData.sysConfig(this.cmb_db.Text);
                if (!InitStaticData.conn.checkDBconn())
                {
                    MessageBox.Show("连接数据库失败，请检查配置参数!");
                    btn1Next.Enabled = true;
                    return;
                }
                btn1Next.Enabled = true;
                tabControl1.SelectedTab = this.tabPage2;
                //查找数据库表
            }
            catch (Exception ce)
            {
                MessageBox.Show("出错!" + ce.Message);
            }
        }
        #endregion

        #region 第二步：生成

        private void tabPage2_Enter(object sender, EventArgs e)
        {
            cmb_classType.SelectedIndex = 1;//默认 public
        }

        private void tb_name_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                bt_search_Click(sender, e);
            }
        }

        /// <summary>
        /// 数据源
        /// </summary>
        private DataTable data = null;

        private const string querysql = @"SELECT COLUMN_NAME as name, COLUMN_TYPE as type, COLUMN_COMMENT as comment
                FROM information_schema.COLUMNS WHERE TABLE_NAME = @tablename ORDER BY ORDINAL_POSITION";

        private const string queryTableNamesql = @"SELECT name FROM sqlite_master WHERE type='table'";

        private const string querysql_sqlite = @"PRAGMA table_info([@tablename])";

        private void bt_search_Click(object sender, EventArgs e)
        {
            //初始化配置文件
            InitStaticData.sysConfig(this.cmb_db.Text);
            if (!InitStaticData.conn.checkDBconn())
            {
                MessageBox.Show("连接数据库失败，请检查配置参数!");
                return;
            }

            if (string.IsNullOrWhiteSpace(tb_name.Text))
            {
                MessageBox.Show("name未填写!");
                return;
            }
            string sql = querysql;
            IDbDataParameter[] sqlParameters;
            if (this.cmb_db.Text == "sqlite")
            {
                sql = querysql_sqlite.Replace("@tablename", tb_name.Text.Trim());
                sqlParameters = null;
            }
            else if (this.cmb_db.Text == "mysql")
            {
                sqlParameters = new MySqlParameter[]
                {
                    new MySqlParameter("@tablename", tb_name.Text.Trim())
                };
            }
            else
            {
                throw new Exception("请选择有效的数据库类型！");
            }
            data = InitStaticData.conn.Select(sql, sqlParameters);
            if (data == null || data.Rows.Count == 0)
            {
                MessageBox.Show("请确认name填写是否正确!");
                return;
            }
            bt_create.Enabled = true;
            if (tb_name.Text.Trim().StartsWith("p_"))
            {
                createType = CreateType.procedure;
                cmb_type.SelectedIndex = 1;
            }
            else
            {
                cmb_type.SelectedIndex = 0;
            }
            if (!string.IsNullOrWhiteSpace(cmb_type.Text))
                if (cmb_type.Text.Trim().Contains("procedure"))
                    createType = CreateType.procedure;
                else
                    createType = CreateType.table;
            else
                createType = CreateType.table;

            dataGridView1.DataSource = data;
        }

        private void btn_back2_Click(object sender, EventArgs e)
        {
            tabControl1.SelectedTab = this.tabPage1;
        }

        private CreateType createType;
        private void bt_create_Click(object sender, EventArgs e)
        {
            try
            {
                bt_create.Enabled = false;
                if (createType == CreateType.table && string.IsNullOrWhiteSpace(InitStaticData.mapperpath))
                {
                    MessageBox.Show("mapperpath未设置,请配置!");
                    return;
                }
                string name = tb_name.Text;
                switch (createType)
                {
                    case CreateType.procedure:
                        string sql = getProcExecSql(name);

                        break;
                    case CreateType.table:
                    default:
                        getTableMapperFile(name);
                        getTableMapperRespositoryFile(name);
                        break;
                }
                this.createTableMapperXmlFile(name, this.GetMapperXmlEntities(name));
                MessageBox.Show("success!");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        #endregion

        private enum CreateType
        {
            table,
            procedure
        }
    }
}
